<?php
/*************************************************************
 *************************************************************
 **
 ** Adobe LiveCycle Collaboration Service Account Management API
 **
 ** Revision
 **   $Revision: #1 $ - $Date: 2010/07/26 $
 **
 ** Author
 **   Raffaele Sena
 **
 ** Copyright
 **
 **   ADOBE SYSTEMS INCORPORATED
 **     Copyright 2007 Adobe Systems Incorporated
 **     All Rights Reserved.
 **
 **   NOTICE: Adobe permits you to use, modify, and distribute this file in accordance with the 
 **   terms of the Adobe license agreement accompanying it.  If you have received this file from a 
 **   source other than Adobe, then your use, modification, or distribution of it requires the prior 
 **   written permission of Adobe.
 **
 *************************************************************
 *************************************************************/

/*****************************************************
 *
 * error thrown or generated by RTC API
 *
 */
class RTCError extends Exception {
}

/*****************************************************
 *
 * Constants for common user roles
 *
 */
interface RTCUserRole {
    const NONE = 0;
    const LOBBY = 5;
    const VIEWER = 10;
    const PUBLISHER = 50;
    const OWNER = 100;
}

/*****************************************************
 *
 * Constants for node configuration
 *
 */
interface NodeConfiguration {
    const STORAGE_SCHEME_SINGLE_ITEM = 0;
    const STORAGE_SCHEME_QUEUE       = 1;
    const STORAGE_SCHEME_MANUAL      = 2;
}

/*****************************************************
 *
 * a class that generates RTC authentication tokens
 *
 */
class RTCAuthenticator {

    private $authURL;

    function __construct($url) {
        $this->authURL = $url;
    }

    //
    // Get an RTC authentication token give login and password.
    //
    function login($user, $password, & $retHeaders) {
        $headers = array (
            "Content-Type" => 'text/xml'
        );

        $data = "<request><username>{$user}</username><password>{$password}</password></request>";

        $resp = RTC::http_post($this->authURL, $data, $headers);

        if (RTC::$DEBUG)
            echo "$resp\n";

        try {
            $result = new SimpleXMLElement($resp);
        } catch (Exception $e) {
            throw new RTCError("bad-response");
        }

        if ($result['status'] == "ok") {
            $auth = $result->authtoken;
            if ($auth['type'] == "COOKIE") {
                $retHeaders["Cookie"] = (string) $auth;
                return null;
            } else {
                $gak = base64_encode($auth);
                return "gak={$gak}";
            }
        } else
            throw new RTCError($resp);
    }

    //
    // Get a guest authentication token.
    //
    function guestLogin($user) {
        $guk = base64_encode("g:{$user}:");
        return "guk={$guk}";
    }
}

/*****************************************************
 *
 * a class that deals with meeting sessions and # external collaboration
 *
 */

class RTCSession {

    private $instance;
    private $account;
    private $room;
    private $secret;

    function __construct($instance, $account, $room) {
        $this->instance = str_replace("#room#", $room, $instance);
        $this->account = $account;
        $this->room = $room;
    }

    /**
     * get an external authentication token
     */
    function getAuthenticationToken($accountSecret, $name, $id, $role) {
        $role = (int) $role;
        if ($role < RTCUserRole::NONE || $role > RTCUserRole::OWNER)
            throw new RTCError("invalid-role");
        $utfname = utf8_encode($name);
        $token = "x:{$utfname}::{$this->account}:{$id}:{$this->room}:{$role}";
        $signature = $this->sign($accountSecret, $token);
        $signed = "{$token}:{$signature}";

        // unencoded
        // $ext = "ext={$signed}";

        // encoded
        $encoded = base64_encode($signed);
        $ext = "exx={$encoded}";

        return $ext;
    }
    
    /**
     * get the userId that the server will generate for this user
     */
    function getUserID($id) {
      return strtoupper("EXT-{$this->account}-{$id}");
    }

    function getSecret($baseURL, $authToken, $authHeaders) {
        $data = RTC::http_get("{$baseURL}app/session?instance={$this->instance}&{$authToken}", $authHeaders);

        if (RTC::$DEBUG)
            echo $data;

        $response = new SimpleXMLElement($data);
        $this->secret = (string) $response-> {
            'session-secret' };
    }

    function invalidate($baseURL, $authToken, $authHeaders) {
        $data = "action=delete&instance={$this->instance}&{$authToken}";
        $res = RTC::http_post("${baseURL}app/session", $data, $authHeaders);

        if (RTC::$DEBUG)
            echo $res;

        $this->instance = null;
        $this->account = null;
        $this->room = null;
        $this->secret = null;
    }

    private function sign($acctSecret, $data) {
        $key = "{$acctSecret}:{$this->secret}";

        // Calculate HMAC-SHA1 according to RFC2104
        // http://www.ietf.org/rfc/rfc2104.txt
        $blocksize = 64;
        $hashfunc = 'sha1';
        if (strlen($key) > $blocksize)
            $key = pack('H*', $hashfunc ($key));
        $key = str_pad($key, $blocksize, chr(0x00));
        $ipad = str_repeat(chr(0x36), $blocksize);
        $opad = str_repeat(chr(0x5c), $blocksize);
        $hmac = pack('H*', $hashfunc (($key ^ $opad) .
        pack('H*', $hashfunc (($key ^ $ipad) . $data))));
        return bin2hex($hmac);
    }
}

/*****************************************************
 *
 * A class that contains room or template item information.
 *
 */
class RTCItem {
    public $name;
    public $desc;
    public $created;

    function __construct($name, $desc, $created) {
        $this->name = $name;
        $this->desc = $desc;
        $this->created = date_create($created);
    }
}

/*****************************************************
 *
 * a class that deals with account information and provisioning
 *
 */
class RTCAccount {
    const ROOM_ITEMS = "meetings";
    const TEMPLATE_ITEMS = "templates";

    public $url;
    private $authToken;
    private $uathHeaders;
    private $authenticator;
    private $baseURL;
    private $contentPath;

    function contentURL() {
        return "{$this->baseURL}app/content{$this->contentPath}";
    }

    function __construct($url) {
        $this->url = $url;
        $this->authToken = null;
        $this->authHeaders = array ();
        $this->authenticator = null;
        $this->baseURL = null;
        $this->contentPath = null;
        $this->roomInstance = null;

        if (RTC::$DEBUG)
            echo RTC::$VERSION . "\n";

        $this->do_initialize();
    }

    function login($user, $password = null) {
        if ($password != null)
            $this->authToken = $this->authenticator->login($user, $password, $this->authHeaders);
        else
            $this->authToken = $this->authenticator->guestLogin($user);

        if (RTC::$DEBUG)
            print_r($this->authHeaders);

        $this->do_initialize();
    }

    //
    // keep the authentication token alive by accessing the account
    //
    function keepalive($user = null, $password = null) {
        $this->contentPath = null;
        if ($this->do_initialize())
            return true;
        if ($user)
            return $this->login($user, $password);
        return false;
    }

    //
    // create a room
    //
    function createRoom($room, $template = null) {
        if (!$template)
            $template = "default";
        $data = "mode=xml&room={$room}&template={$template}&{$this->authToken}";
        return RTC::http_post($this->url, $data, $this->authHeaders);
    }

    //
    // list rooms or templates
    //
    function listItems($type = null) {
        if ($type != RTCAccount::TEMPLATE_ITEMS)
            $type = RTCAccount::ROOM_ITEMS;

        $items = array ();
        $data = RTC::http_get("{$this->contentURL()}/{$type}/?{$this->authToken}", $this->authHeaders);

        try {
            $xml = new SimpleXMLElement($data);
        } catch (Exception $e) {
            throw new RTCError("bad-response");
        }

        foreach ($xml->children->node as $n) {
            $name = $n->name;
            $desc = $n->properties->xpath('property[@name = "cr:description"]');
            if (count($desc) > 0)
                $desc = $desc[0]->value;
            else
                $desc = null;
            $created = $n->properties->xpath('property[@name = "jcr:created"]');
            if (count($created) > 0)
                $created = $created[0]->value;
            else
                $created = null;

            $items[] = new RTCItem($name, $desc, $created);
        }

        return $items;
    }

    //
    // delete a room or a template
    //
    function delete($item, $type = null, $list = FALSE) {
        if ($type != RTCAccount::TEMPLATE_ITEMS)
            $type = RTCAccount::ROOM_ITEMS;

        $curl = "{$this->contentURL()}/{$type}/{$item}";
        $limitCount = $list ? "" : "&count=0";
        $data = "action=delete&response=inline{$limitCount}&{$this->authToken}";
        return RTC::http_post($curl, $data, $this->authHeaders);
    }

    //
    // list rooms
    //
    function listRooms() {
        return $this->listItems(RTCAccount::ROOM_ITEMS);
    }

    //
    // list templates
    //
    function listTemplates() {
        return $this->listItems(RTCAccount::TEMPLATE_ITEMS);
    }

    //
    // delete a room
    //
    function deleteRoom($r, $list = FALSE) {
        if ($r == null)
            throw new RTCError("parameter-required");
        return $this->delete(strtolower($r), RTCAccount::ROOM_ITEMS, $list);
    }

    //
    // delete a template
    //
    function deleteTemplate($t, $list = FALSE) {
        if ($t == null)
            throw new RTCError("parameter-required");
        return delete($t, RTCAccount::TEMPLATE_ITEMS, $list);
    }

    //
    // Returns a room session for external authentication
    //
    function getSession($room) {
        $parts = explode('/', $this->url);
        $session = new RTCSession($this->roomInstance, $parts[count($parts) - 1], $room);
        $session->getSecret($this->baseURL, $this->authToken, $this->authHeaders);
        return $session;
    }

    //
    // invalidate room session
    //
    function invalidateSession($session) {
        $session->invalidate($this->baseURL, $this->authToken, $this->authHeaders);
    }

    //
    // Register endpoint URL for webhooks
    //
    function registerHook($endpoint = null, $token = null) {
        $acctid = explode('/', $this->roomInstance);
        $acctid = $acctid[0];
        $data = "account={$acctid}&action=registerhook";
        if ($endpoint != null)
            $data .= "&endpoint=" . urlencode($endpoint);
        if ($token)
            $data .= "&token=" . urlencode($token);
        $data .= "&{$this->authToken}";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }
    
    //
    // Unregister endpoint URL
    //
    function unregisterHook() {
        return $this->registerHook();
    }

    // Return Registered Hook Info
    function getHookInfo() {
        $acctid = explode('/', $this->roomInstance);
        $acctid = $acctid[0];
        return RTC::http_get("{$this->baseURL}app/rtc?action=hookinfo&account={$acctid}&{$this->authToken}", $this->authHeaders);
    }

    //
    // Subscribe to collection events
    //
    function subscribeCollection($room, $collection, $nodes = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $params = "collection={$collection}";
        if ($nodes != null) {
            if (!is_array($nodes))
                $nodes = array (
                    $nodes
                );
            while (list ($i, $n) = each($nodes)) {
                $params .= "&node={$v}";
            }
        }
        $data = "&instance=$instance&action=subscribe&$params&$this->authToken";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }

    //
    // Unubscribe from collection events
    //
    function unsubscribeCollection($room, $collection, $nodes = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $params = "collection={$collection}";
        if ($nodes != null) {
            if (!is_array($nodes))
                $nodes = array (
                    $nodes
                );
            while (list ($i, $n) = each($nodes)) {
                $params .= "&node={$n}";
            }
        }
        $data = "instance={$instance}&action=subscribe&{$params}&{$this->authToken}";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }

    //
    // Create node or collection
    //
    function createNode($room, $coll, $node, $configuration = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
	$params = "instance={$instance}&action=configure&collection={$coll}&node={$node}&{$this->authToken}";

	if ($configuration != null) {
          $headers = array_merge($this->authHeaders, array ( "Content-Type" => 'text/xml' ));
          $data = "<request>" . RTC::array_toXML($configuration, "configuration") . "</request>";
          return RTC::http_post("{$this->baseURL}app/rtc?{$params}", $data, $headers);
        } else {
          return RTC::http_post("{$this->baseURL}app/rtc", $params, $this->authHeaders);
	}
    }

    //
    // Remove a node or collection
    //
    function removeNode($room, $coll, $node = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
	$data = "instance={$instance}&action=remove&collection={$coll}";
	if ($node != null)
	    $data .= "&node={$node}";

	$data .= "&{$this->authToken}";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }

    //
    // Configure a node
    //
    function setNodeConfiguration($room, $coll, $node, $configuration) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
	$params = "instance={$instance}&action=configure&collection={$coll}&node={$node}&{$this->authToken}";

        $headers = array_merge($this->authHeaders, array ( "Content-Type" => 'text/xml' ));
        $data = "<request>" . RTC::array_toXML($configuration, "configuration") . "</request>";
        return RTC::http_post("{$this->baseURL}app/rtc?{$params}", $data, $headers);
    }

    //
    // Return the node configuration
    // """
    function getNodeConfiguration($room, $coll, $node) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $path = "/{$coll}/nodes/{$node}/configuration";
        return RTC::http_get("{$this->baseURL}app/rtc?instance={$instance}&path={$path}&{$this->authToken}", $this->authHeaders);
    }

    //
    // Return the RTC items given collection and node
    //
    function fetchItems($room, $coll, $node, $items = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $params = "instance={$instance}&collection=${coll}&node={$node}";
        if ($items != null) {
            if (!is_array($items))
                $items = array (
                    $items
                );
            while (list ($i, $it) = each($items)) {
                $params .= "&item={$it}";
            }
        }
        $params .= "&{$this->authToken}";
        return RTC::http_get("{$this->baseURL}app/rtc?{$params}", $this->authHeaders);
    }

    //
    // Publish an item
    //
    function publishItem($room, $collection, $node, $item, $overwrite = false) {
        $headers = array_merge($this->authHeaders, array ( "Content-Type" => 'text/xml' ));

        $instance = str_replace("#room#", $room, $this->roomInstance);
        $params = "instance={$instance}&action=publish&collection={$collection}&node={$node}";
        if ($overwrite) $params .= "&overwrite={$overwrite}";
        $params .= "&{$this->authToken}";
        $data = "<request>" . RTC::array_toXML($item, "item") . "</request>";

        return RTC::http_post("{$this->baseURL}app/rtc?{$params}", $data, $headers);
    }

    //
    // Retract an item
    //
    function retractItem($room, $collection, $node, $itemID) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $data = "instance={$instance}&collection={$collection}&node={$node}&item={$itemID}&{$this->authToken}";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }

    //
    // Set user role
    //
    function setUserRole($room, $userID, $role, $coll = null, $node = null) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
	$data = "instance={$instance}&action=setrole&user={$userID}&role={$role}";

	if ($coll != null)
	    $data .= "&collection={$coll}";
	if ($coll != null)
	    $data .= "&node={$node}";

	$data .= "&{$this->authToken}";
        return RTC::http_post("{$this->baseURL}app/rtc", $data, $this->authHeaders);
    }

    //
    // Returns information about the account, if active
    //
    function getAccountInfo() {
        $acctid = explode('/', $this->roomInstance);
        $acctid = $acctid[0];
        $data = RTC::http_get("{$this->baseURL}app/account?account={$acctid}&{$this->authToken}", $this->authHeaders);
        return $data;
    }

    //
    // Returns information about the room/instance, if active
    //
    function getRoomInfo($room) {
        $instance = str_replace("#room#", $room, $this->roomInstance);
        $data = RTC::http_get("{$this->baseURL}app/account?instance={$instance}&{$this->authToken}", $this->authHeaders);
        return $data;
    }

    private function do_initialize() {
        if ($this->contentPath)
            return true;

        $data = RTC::http_get("{$this->url}?mode=xml&accountonly=true&{$this->authToken}", $this->authHeaders);

        if (RTC::$DEBUG)
            echo $data;

        try {
            $xml = new SimpleXMLElement($data);
        } catch (Exception $e) {
            throw new RTCError("bad-response");
        }

        if ($xml->getName() == "meeting-info") {
            $this->baseURL = '' . $xml->baseURL['href'];
            $this->url = rtrim($this->baseURL, '/') . parse_url($this->url, PHP_URL_PATH);
            $this->contentPath = '' . $xml->accountPath['href'];
            if ($xml->room)
                $this->roomInstance = '' . $xml->room['instance'];
            return true;
        }

        if ($xml->getName() == "result") {
            if ($xml['code'] == "unauthorized") {
                if ($xml->baseURL) {
		    $this->baseURL = '' . $xml->baseURL['href'];
		    $this->url = rtrim($this->baseURL, '/') . parse_url($this->url, PHP_URL_PATH);
                }

                $authURL = '' . $xml->authentication['href'];
                if (substr($authURL, 0, 1) == '/') {
                    $authURL = $this->baseURL . $authURL;
                }
                $this->authenticator = new RTCAuthenticator($authURL);
                return false;
            }
        }

        throw new RTCError($data);
    }
}

class RTC {
    public static $DEBUG = false;
    public static $USE_CURL = false;
    public static $VERSION = '$Revision: #1 $ - $Date: 2010/07/26 $';

    function http_get($url, $headers = null) {
        if (RTC::$DEBUG) {
            echo "http_get: {$url}\n";
            if ($headers != null)
                print_r($headers);
        }

        if (RTC::$USE_CURL) {
            /*
             * use curl library
             */
            $req = curl_init($url);
            //if (RTC::$DEBUG) curl_setopt($req, CURLOPT_VERBOSE, true);
            curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($req, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($req, CURLOPT_SSL_VERIFYPEER, false);

            if ($headers)
                curl_setopt($req, CURLOPT_HTTPHEADER, $headers);

            $resp = curl_exec($req);
            $error = curl_error($req);
            if ($error == "")
                $status = curl_getinfo($req, CURLINFO_HTTP_CODE);
            else
                $status = $error;

            curl_close($req);

            if ($error != "" && $status != 200)
                throw new RTCError($error);
            else
                return $resp;
        } else {
            /*
             * use streams
             */
            if ($headers == null)
                $header = '';
            else {
                $header = "";

                foreach ($headers as $name => $value) {
                    if ($header != "")
                        $header .= "\r\n";
                    $header = $header . $name . ": " . $value;
                }
            }

            $opts = array (
                'http' => array (
                    'method' => 'GET',
                    'header' => $header
                )
            );

            $context = stream_context_create($opts);

            $fp = fopen($url, 'r', false, $context);
            if (!$fp)
                throw new RTCError("connection-failed");

            $meta = stream_get_meta_data($fp);
            $statusLine = explode(' ', $meta['wrapper_data'][0], 3);

            $resp = stream_get_contents($fp);
            fclose($fp);

            if ($statusLine[1] == "200" | $statusLine[1] == "302")
                return $resp;
            else
                throw new RTCError($statusLine[1]);
        }
    }

    function http_post($url, $params, $headers = null) {
        
        
        if (is_array($params))
            $data = http_build_query($params);
        else
            $data = $params;

        if (RTC::$DEBUG) {
            echo "http_post: {$url} {$data}\n";
            if ($headers != null)
                print_r($headers);

        }
        
        //$resp = "<h1>Request Successful</h1><br><p>" . "http_post: {$url} {$data}";

        if (RTC::$USE_CURL) {
            /*
             * use curl library
             */
            $req = curl_init($url);
            //if (RTC::$DEBUG) curl_setopt($req, CURLOPT_VERBOSE, true);
            curl_setopt($req, CURLOPT_RETURNTRANSFER, true);
            curl_setopt($req, CURLOPT_FOLLOWLOCATION, true);
            curl_setopt($req, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($req, CURLOPT_POST, true);
            curl_setopt($req, CURLOPT_POSTFIELDS, $data);

            if ($headers)
                curl_setopt($req, CURLOPT_HTTPHEADER, $headers);

            $resp = curl_exec($req);
            $error = curl_error($req);
            if ($error == "")
                $status = curl_getinfo($req, CURLINFO_HTTP_CODE);
            else
                $status = $error;

            curl_close($req);

            if ($error != "" && $status != 200)
                throw new RTCError($error);
            
        } else {
            /*
             * use streams
             */

            if ($headers == null)
                $header = 'Content-type: application/x-www-form-urlencoded';
            else {
                $header = "";

                foreach ($headers as $name => $value) {
                    if ($header != "")
                        $header .= "\r\n";
                    $header = $header . $name . ": " . $value;
                }
            }

            $opts = array (
                'http' => array (
                    'method' => 'POST',
                    'header' => $header,
                    'content' => $data
                )
            );

            $context = stream_context_create($opts);

            $fp = fopen($url, 'r', false, $context);
            if (!$fp)
                throw new RTCError("connection-failed");

            $meta = stream_get_meta_data($fp);
            $statusLine = explode(' ', $meta['wrapper_data'][0], 3);

            $resp = stream_get_contents($fp);
            fclose($fp);

            if ($statusLine[1] != "200")
                throw new RTCError($statusLine[1]);
        }
        
        return $resp;
    }

    function array_toXML($a, $root = null) {
        $result = '';
        if ($root != null) {
            if ($root == 'item') {
                 $id = array_key_exists('itemID', $a) ? $a['itemID'] : 'item';
                $result .= "<{$root} id=\"{$id}\">";
            } else
                $result .= "<{$root}>";
        }
        foreach($a as $name => $value) {
            if ($root == 'configuration')
                $result .= "<field var=\"{$name}\">";
            else
                $result .= "<property name=\"{$name}\">";
            $result .= '<value type="';
            if (is_array($value))
                 $result .= 'object';
            else if (is_string($value))
                 $result .= 'string';
            else if (is_float($value))
                 $result .= 'double';
            else if (is_int($value))
                 $result .= 'int';
            else if (is_bool($value))
                 $result .= 'boolean';
            else
                 //
                 // TODO: check for XML and binary data
                 //
                 $result .= 'unknown';
            $result .= '">';
            if (is_array($value))
                $result .= RTC::array_toXML($value);
            else if (is_string($value))
                $result .= $value;
            else
                $result .= var_export($value, true);
            $result .= '</value>';
            if ($root == 'configuration')
                $result .= '</field>';
            else
                $result .= '</property>';
        }
        if ($root != null)
            $result .= "</{$root}>";

        return $result;
    }
}

if (!isset($_SERVER['QUERY_STRING'])) {

  function usage($progname) {
    echo "usage: {$progname} [--debug] [--host=url] account user password command parameters...\n";
    echo "\n";
    echo "where <command> is:\n";
    echo "    --list\n";
    echo "    --create room [template]\n";
    echo "    --delete room\n";
    echo "    --delete-template template\n";
    echo "    --ext-auth secret room username userid role\n";
    echo "    --invalidate room\n";
    echo "\n";
    echo "    --get-node-configuration room collection node\n";
    echo "    --fetch-items room collection node\n";
    echo "    --register-hook endpoint [token]\n";
    echo "    --unregister-hook\n";
    echo "    --hook-info\n";
    echo "    --subscribe-collection room collection\n";
    echo "    --unsubscribe-collection room collection\n";
    echo "    --create-node room collection [node]\n";
    echo "    --remove-node room collection [node]\n";
    echo "    --set-user-role room userID role [collection [node]]\n";
    echo "    --publish-item room collection node itemID body\n";
    echo "    --retract-item room collection node itemID\n";
    exit(1);
  }

  function getRole($role) {
    $role = strtolower($role);
    if ($role == "none")
      return RTCUserRole::NONE;
    else if ($role == "lobby")
      return RTCUserRole::LOBBY;
    else if ($role == "viewer")
      return RTCUserRole::VIEWER;
    else if ($role == "publisher")
      return RTCUserRole::PUBLISHER;
    else if ($role == "owner")
      return RTCUserRole::OWNER;
    else if (is_numeric($role))
      return intval($role);
    else
      throw new RTCError("invalid-role");
  }

  //
  // running from the command line
  //
  $args = $_SERVER['argv'];
  $progname = array_shift($args);

  $host = "http://connectnow.acrobat.com";
  $accountName = "<YOUR DEVELOPER ACCOUNT NAME>";
  $username = "sdkuser";
  $password = "sdkpassword";

  while (count($args) > 0) {
    $arg = $args[0];

    if ($arg == "--debug")
      RTC::$DEBUG = true;

    else if (strncmp($arg, "--host=", 7) == 0)
      $host = substr($arg, 7);

    else if (strncmp($arg, "-", 1) == 0) {
      echo "invalid option: $arg\n";
      $args = array();
    }

    else 
      break;

    array_shift($args);
  }

  if (count($args) < 3) {
    usage($progname);
  }

  $accountName = array_shift($args);
  $username = array_shift($args);
  $password = array_shift($args);

  $host = rtrim($host, '/');
  $accountURL = "{$host}/${accountName}";

  try {
    $am = new RTCAccount($accountURL);
    $am->login($username, $password);

    if (count($args) == 0 || $args[0] == "--list") {
      echo "==== template list for {$accountName} ====\n";
      foreach ($am->listTemplates() as $t) {
        echo "{$t->name}:{$t->created->format(DATE_RFC822)}\n";
      }

      echo "==== room list for {$accountName} ====\n";
      foreach ($am->listRooms() as $r) {
        echo "{$r->name}:{$r->desc}:{$r->created->format(DATE_RFC822)}\n";
      }
    }
  
    else if ($args[0] == "--create") {
      $am->createRoom($args[1], count($args) > 2 ? $args[2] : null);
    }

    else if ($args[0] == "--delete") {
      $am->deleteRoom($args[1]);
    }

    else if ($args[0] == "--delete-template") {
      $am->deleteTemplate($args[1]);
    }

    else if ($args[0] == "--ext-auth") {
      if (count($args) >= 6)
        $role = getRole($args[5]);
      else
        $role = RTCUserRole::LOBBY;
      $session = $am->getSession($args[2]);
      $token = $session->getAuthenticationToken($args[1], $args[3], $args[4], $role);
      echo $token . "\n";
    }

    else if ($args[0] == "--info") {
      if (count($args) == 1) {
        echo $am->getAccountInfo();
      } else {
        echo $am->getRoomInfo($args[1]);
      }
    }

    else if ($args[0] == "--get-node-configuration") {
      echo $am->getNodeConfiguration($args[1], $args[2], $args[3]);
    }

    else if ($args[0] == "--fetch-items") {
      echo $am->fetchItems($args[1], $args[2], $args[3]);
    }

    else if ($args[0] == "--register-hook") {
      if (count($args) > 2)
        echo $am->registerHook($args[1], $args[2]);
      else
        echo $am->registerHook($args[1]);
    }
    
    else if ($args[0] == "--unregister-hook") {
      echo $am->unregisterHook();
    }
    
    else if ($args[0] == "--hook-info") {
      echo $am->getHookInfo();
    }
    
    else if ($args[0] == "--subscribe-collection") {
      if (count($args) > 3)
        echo $am->subscribeCollection($args[1], $args[2], $args[3]);
      else
        echo $am->subscribeCollection($args[1], $args[2]);
    }
    
    else if ($args[0] == "--unsubscribe-collection") {
      if (count($args) > 3)
        echo $am->unsubscribeCollection($args[1], $args[2], $args[3]);
      else
        echo $am->unsubscribeCollection($args[1], $args[2]);
    }
    
    else if ($args[0] == "--publish-item") {
      echo $am->publishItem($args[1], $args[2], $args[3], 
        array( 'itemID' => $args[4], 'body' => $args[5] ));
    }

    else if ($args[0] == "--retract-item") {
      echo $am->retractItem($args[1], $args[2], $args[3], $args[4]);
    }

    else if ($args[0] == "--create-node") {
      echo $am->createNode($args[1], $args[2], $args[3]);
    }

    else if ($args[0] == "--remove-node") {
      if (count($args) > 3)
        echo $am->removeNode($args[1], $args[2], $args[3]);
      else
        echo $am->removeNode($args[1], $args[2]);
    }
    
    else if ($args[0] == "--set-user-role") {
      $role = getRole($args[3]);

      if (count($args) > 5)
        echo $am->setUserRole($args[1], $args[2], $role, $args[4], $args[5]);
      else if (count($args) > 4)
        echo $am->setUserRole($args[1], $args[2], $role, $args[4]);
      else
        echo $am->setUserRole($args[1], $args[2], $role);
    }

    else {
      usage($progname);
    }

  } catch(RTCError $e) {
    echo "Error: {$e}";
  }
}

?>
